package linkage2;

import static java.lang.Math.pow;

public class CalcLinkage {
	
	/**********************************************************
	 * for test purpose only!!!!!
	 * TODO: delete!!!!
	 */
	
	public double[][] getArr() { return answaresTable; }
	
	/**********************************************************
	 * till here - for test purpose only!!!!!
	 * TODO: delete till here!!!!
	 */
	
	public CalcLinkage() {
		numOfFounders = 2;
		numOfNonFounders = 1;
		founders2Id = new short[] {0, 1};
		nonfounders2Id = new short[] {2};
		id2Arrays = new short[] {0, 1, 0};
		int numOfMarkers = markersData.length / 2;
		// TODO: arrange the ped by topological order
		currAllele = new short[(numOfFounders + numOfNonFounders) * 2];
		
		answaresTable = new double[numOfMarkers][(int)pow(2, numOfNonFounders * 2)]; // TODO: verify if array works only with int!!!!!!
		
		for(int marker = 0; marker < numOfMarkers; marker++) {
			calcColForMarker(marker);
		}
	}
	
	private void calcColForMarker(int marker) {
		int nonFoundersLoopLimit = (int)pow(2, numOfNonFounders * 2);
		long foundersLoopLimit = (long)pow(2, numOfFounders - 1);
		for(nonfoundersVector = 0; nonfoundersVector < nonFoundersLoopLimit; nonfoundersVector++) {
			assignFoundersByVector(marker);
			for(foundersVector = 0; foundersVector < foundersLoopLimit; foundersVector++) {
				if(isLegalAssignForVectors(marker)) {
					answaresTable[marker][nonfoundersVector]++;
				}
			}
		}
		// turn the number of options matrix into probability matrix
		double foundersProb = 1; // the probability of the founders allele in the current marker
		int founderLoc; // the location of the first allele of the current founder in the currAllele array
		for(int founder = 0; founder < numOfFounders; founder++) {
			founderLoc = founders2Id[founder] * 2;
			if(currAllele[founderLoc] == currAllele[founderLoc + 1]) { foundersProb *= 0.25; }
			else { foundersProb *= 0.5; }
		}
		double tmp = foundersProb / (nonFoundersLoopLimit * foundersLoopLimit);
		for(nonfoundersVector = 0; nonfoundersVector < nonFoundersLoopLimit; nonfoundersVector++) {
			answaresTable[marker][nonfoundersVector] = answaresTable[marker][nonfoundersVector] * tmp;
		}
	}
	
	private boolean isLegalAssignForVectors(int marker) {
		int markerPos = 2 * marker; // the position of the first allele of the marker
		int id, id2;
		for(int i = 0; i < nonfounders2Id.length; i++) {
			id = nonfounders2Id[i];
			id2 = 2 * id;
			currAllele[id2] = (nonfoundersVector & (1 << id2Arrays[id] * 2)) == 0 ?
					currAllele[2 * pedArr[fatherCell][id]] : currAllele[2 * pedArr[fatherCell][id] + 1];
			currAllele[id2 + 1]= (nonfoundersVector & (1 << id2Arrays[id] * 2 + 1)) == 0 ?
					currAllele[2 * pedArr[motherCell][id]] : currAllele[2 * pedArr[motherCell][id] + 1];
			if((markersData[markerPos][id] != currAllele[id2] || markersData[markerPos + 1][id] != currAllele[id2 + 1]) &&
					(markersData[markerPos + 1][id] != currAllele[id2] || markersData[markerPos][id] != currAllele[id2 + 1])) {
				return false;
			}
		}
		return true;
	}
	
	/*
	 * assign the alleles by which allele came from the father and which allele came
	 * from the mother according to the inheritance vector
	 */
	private void assignFoundersByVector(int marker) {
		int markerPos = 2 * marker;
		int id;
		for(int i = 0; i < founders2Id.length; i++) {
			id = founders2Id[i];
			if((foundersVector & (1 << i)) == 0) { // the first allele is the paternal allele
				currAllele[2 * id] = markersData[markerPos][id];
				currAllele[2 * id + 1] = markersData[markerPos + 1][id];
			} else { // the second allele is the maternal allele 
				currAllele[2 * id] = markersData[markerPos + 1][id];
				currAllele[2 * id + 1] = markersData[markerPos][id];
			}
		}
	}
		
	private short numOfFounders; // TODO: private + assign correct value
	private short numOfNonFounders; // TODO: private + assign correct value
	// pedArr[i][j] - the i'th cell of person j
	// cells - (0)id; (1)father; (2)mother; (3)sex; (4)status
	private short[][] pedArr = { {0, 1, 2}, {-1, -1, 0}, {-1, -1, 1}, {0, 1, 0}, {0, 0, 0} }; // TODO: private + assign correct value
	// markersData[2i][j] - first allele of person j of marker i
	private short[][] markersData = { {1, 0, 1}, {1, 0, 0} }; // TODO: private + assign correct value
	// founderArr[i] - the id (in pedArr) of the i'th founder
	private short[] founders2Id; // TODO: private + assign correct value
	private short[] nonfounders2Id; // TODO: private + assign correct value
	// converts between a persons ID and his/her place at the founders / non-founders array
	private short[] id2Arrays; // TODO: private + assign correct value
	
	private long foundersVector; // TODO: check the limitation it causes to the number of founders!!!!
	private int nonfoundersVector; // TODO: check the limitation it causes to the number of non-founders!!!!
	
	// a vector of size 2 * number of individuals in the ped. currAllele[2i] is the allele that person i got 
	// from his father, and currAllele[2i+1] is the allele that i got from his mother
	private short currAllele[];
	
	// cells of pedArr:
	private final int idCell = 0;
	private final int fatherCell = 1;
	private final int motherCell = 2;
	private final int sexCell = 3;
	private final int statusCell = 4;
	
	// TODO: rename the table to a better name!!!!! TODO: write a better comment!!!!!
	// this is the result table.
	// answaresTanle[i][j] - means the probability of the i'th marker with 'j' inheritance vector (for the non-founders). 
	private double[][] answaresTable;
}
